package com.syh.core.ddd.infrastructure.repository;

import cn.redscarf.commons.util.bean.Holder;
import cn.redscarf.commons.util.reflection.ExtReflectionUtils;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.syh.core.expction.UnexpectedlyException;
import com.syh.jn.scm.infrastructure.repository.utils.MybatisTemplate;
import com.syh.jn.scm.infrastructure.wrapper.BaseResolve;
import com.syh.jn.scm.infrastructure.wrapper.ResolveToSQL;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.ibatis.jdbc.SQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AggregateRepoMybatisImplTmpl<ListType, Model, ID extends Serializable> {
  private Logger logger = LoggerFactory.getLogger(this.getClass());

  private Class<Model> modelClass;
  private Class<ListType> listTypeClass;

  @Getter
  private SQL querySql;
  @Getter
  private BaseResolve.ParseMethods wrapperParseMethods = null;

  private AggregateRepoMybatisImplTmpl(Builder builder) {
    this.modelClass = builder.modelClass;
    this.listTypeClass = builder.voClass;

    // 初始化默认的sql语句
    String tableName = this.getModelTableName(this.modelClass);
    this.querySql = new SQL() {{
      SELECT("*");
      FROM(tableName);
    }};
  }

  public ListType getById(ID id) {
    String keyName = this.getModelTableKey(this.modelClass);
    return this.getOne(Wrapper.build().append(Restrictions.eq(keyName, id)));
  }

  public ListType getOne(Wrapper wrapper) {
    List<ListType> voList = this.findListPage(wrapper, 1, 1);
    return voList.isEmpty() ? null : voList.get(0);
  }

  public List<ListType> getList(Wrapper wrapper) {
    return this.findListPage(wrapper, 1, Integer.MAX_VALUE);
  }

  public boolean contains(ID id) {
    return this.getById(id) == null;
  }


  public List<ListType> findListPage(Wrapper wrapper, Integer startRow, Integer count) {
    return this.superFindListPage(wrapper, startRow, count);
  }

  public int getCount(Wrapper wrapper) {
    StringBuffer sqlSb = new StringBuffer();
    String viewSql = this.querySql.toString();

    String viewSqlAry[] = viewSql.split("\\s*FROM\\s*");
    sqlSb.append("SELECT COUNT(*) AS COUNT FROM ");
    for (int i = 1; i <= viewSqlAry.length - 1; i++) {
      if (i > 1) {
        sqlSb.append(" FROM ").append(viewSqlAry[i]);
      } else {
        sqlSb.append(viewSqlAry[i]);
      }
    }
    BaseResolve resolveToSQL;
    if (this.wrapperParseMethods == null) {
      resolveToSQL = new ResolveToSQL(wrapper);
    } else {
      resolveToSQL = new ResolveToSQL(wrapper, this.wrapperParseMethods);
    }
    sqlSb.append(" WHERE 1=1 ");
    sqlSb.append(resolveToSQL.resolve());

    Map resultM = MybatisTemplate.selectOne(sqlSb.toString());
    return Integer.parseInt(resultM.get("COUNT").toString());
  }

  /**
   * 内部方法，所有Sql执行的列表查询都调用本方法 改方法是 Mybatis 实现 注意增删改 业务不能调用该接口数据
   *
   * @param wrapper
   * @param startRow
   * @param count
   * @return
   */
  protected List<ListType> superFindListPage(Wrapper wrapper, Integer startRow, Integer count) {
    StringBuffer sqlString = new StringBuffer(this.querySql.toString());

    BaseResolve resolveToSQL;
    if (this.wrapperParseMethods == null) {
      resolveToSQL = new ResolveToSQL(wrapper);
    } else {
      resolveToSQL = new ResolveToSQL(wrapper, this.wrapperParseMethods);
    }

    sqlString.append(" WHERE 1=1 ");
    sqlString.append(resolveToSQL.resolve());
    if (startRow != null && count != null) {
      sqlString.append(" LIMIT ").append((startRow - 1) * count).append(",").append(count);
    }
    List<Map<String, Object>> list = MybatisTemplate.selectList(sqlString.toString());

    List<ListType> categoryVoList = JSONArray.parseArray(JSONObject.toJSONString(list), this.listTypeClass);
    return categoryVoList;
  }


  /**
   * 获取对应数据模型的表名
   *
   * @param clzz
   * @return
   */
  public String getModelTableName(Class clzz) {
    Annotation tableAnnotation = clzz.getAnnotation(Table.class);
    if (tableAnnotation == null) {
      throw new UnexpectedlyException("类不属于数据库模型");
    }

    return ((Table) tableAnnotation).name();
  }

  /**
   * 获取对应模型的表的主键
   *
   * @param clzz
   * @return
   */
  public String getModelTableKey(Class clzz) {
    Annotation tableAnnotation = clzz.getAnnotation(Table.class);
    if (tableAnnotation == null) {
      throw new UnexpectedlyException("类不属于数据库模型");
    }

    Holder<Field> holder = new Holder();
    ExtReflectionUtils.doWithFields(clzz, (field) -> {
      if (field.getAnnotation(Id.class) != null) {
        holder.setValue(field);
      }
    });

    if (holder.getValue() == null) {
      throw new UnexpectedlyException("找不到对应的表主键");
    }
    return holder.getValue().getName();
  }

  public AggregateRepoMybatisImplTmpl<ListType, Model, ID> setQuerySql(SQL querySql) {
    this.querySql = querySql;
    return this;
  }

  public AggregateRepoMybatisImplTmpl<ListType, Model, ID> setWrapperParseMethods(BaseResolve.ParseMethods wrapperParseMethods) {
    this.wrapperParseMethods = wrapperParseMethods;
    return this;
  }

  public static final class Builder<ListType, Model, ID extends Serializable> {
    private Class<Model> modelClass;
    private Class<ListType> voClass;

    public Builder() {
    }

    public Builder modelClass(Class<Model> val) {
      this.modelClass = val;
      return this;
    }

    public Builder listTypeClass(Class<ListType> val) {
      this.voClass = val;
      return this;
    }


    public AggregateRepoMybatisImplTmpl build() {
      return new AggregateRepoMybatisImplTmpl(this);
    }
  }
}
